use crate::sym_type::{SymType, Width};

#[derive(Debug)]
pub struct LLVMProgram {
    pub glob_var: Vec<GlobalVar>,
    pub func_decl: Vec<FuncDecl>,
    pub func_def: Vec<FuncDef>,
}

#[derive(Debug)]
pub struct GlobalVar {
    pub var_id: String,
    pub var_type: SymType,
    pub init_types: Vec<SymType>,
    pub init_vals: Vec<String>,
}

#[derive(Debug)]
pub struct FuncDecl {
    pub func_id: String,
    pub func_type: SymType,
    pub ftypes: Vec<SymType>,
}

#[derive(Debug)]
pub struct FuncDef {
    pub func_type: SymType,
    pub func_name: String,
    pub fparams: Vec<FParam>,
    pub blocks: Vec<BasicBlock>, 
    pub allocs: Vec<(Instr, String)>,                  // 局部变量内存申请在函数首部进行
}

#[derive(Debug)]
pub struct FParam {
    pub param_name: String,
    pub param_type: SymType,
}

#[derive(Debug)]
pub struct BasicBlock {
    pub block_label: String,
    pub phi_instr: Vec<Instr>,              // phi指令，需在基本块首部定义
    pub instrs: Vec<Instr>,                 // 普通指令
    pub ter_instr: Option<Instr>,           // 基本块终结指令
    pub instr_cnt: usize,                   // 此基本块之前的所有基本块的指令数量
    pub depth: usize,                       // 此基本块的循环嵌套深度
}

#[derive(Debug, PartialEq)]
pub enum InstrType {
    Add,
    Sub,
    Mul,
    Sdiv,
    Srem,
    Fadd,
    Fsub,
    Fmul,
    Fdiv,
    Zext,
    Sitofp,
    Fptosi,
    Icmp,
    Fcmp,
    Phi,
    Alloca,
    Store,
    Load,
    Call,
    GetElemPtr,
    BitCast,
    Comment,
    Return,
    Branch,
}

#[derive(Debug)]
pub enum Instr {
    // 算术运算
    Add(BinaryOp), // int
    Sub(BinaryOp),
    Mul(BinaryOp),
    Sdiv(BinaryOp),
    Srem(BinaryOp),
    Fadd(BinaryOp), // float
    Fsub(BinaryOp),
    Fmul(BinaryOp),
    Fdiv(BinaryOp),
    // 类型转换
    Zext(ConverOp),
    Sitofp(ConverOp),
    Fptosi(ConverOp),
    // 比较运算
    Icmp(String, BinaryOp),
    Fcmp(String, BinaryOp),
    // Phi
    Phi(String, SymType, Vec<(String, String)>),
    // 变量
    Alloca{result: String, ty: SymType, len: String},
    Store{ty: SymType, value: String, ptr: String, len: String},
    Load{result: String, ty: SymType, ptr: String, len: String},
    // 函数
    Call(String, String, SymType, Vec<(String, SymType)>),
    // 数组
    GetElemPtr(String, SymType, String, Vec<String>),
    BitCast(String, SymType, String, SymType),
    // 注释
    Comment(String),
    // 终结指令
    Return(SymType, Option<String>),
    Branch(Option<String>, String, Option<String>),
}

#[derive(Debug)]
pub struct BinaryOp {
    pub result: String,
    pub ty: SymType,
    pub op1: String,
    pub op2: String,
}

#[derive(Debug)]
pub struct ConverOp {
    pub result: String,
    pub ty: SymType,
    pub value: String,
    pub ty2: SymType,
}

impl LLVMProgram {
    pub fn new() -> Self {
        LLVMProgram {
            glob_var: Vec::new(),
            func_decl: Vec::new(),
            func_def: Vec::new(),
        }
    }
}

impl BinaryOp {
    pub fn new() -> Self {
        BinaryOp{
            result: String::from(""),
            ty: SymType::new(Width::Void, true),
            op1: String::from(""),
            op2: String::from(""),
        }
    }
}

impl ConverOp {
    pub fn new() -> Self {
        ConverOp{
            result: String::from(""),
            ty: SymType::new(Width::Void, true),
            value: String::from(""),
            ty2: SymType::new(Width::Void, true),
        }
    }
}

